package com.example.kafka.kerberos.login;

import com.sun.security.auth.module.Krb5LoginModule;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.login.LoginException;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * Java登录kerberos的2中方式验证
 * 1.使用keytab
 * 2.使用用户/密码
 *
 * @author stone
 * @version $1.0
 * @date 2020/9/11 15:18:45
 */
public class JavaKerberosClient {

    final Subject subject = new Subject();
    final Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
    final Map<String, Object> sharedState = new HashMap<>();
    final Map<String, String> optionMap = new HashMap<>();

    public static void main(String[] args) throws Exception {
        // login
        final JavaKerberosClient javaClient = new JavaKerberosClient();
        javaClient.loginImplWithKeytab();
//        javaClient.loginImplWithPassword();
    }

    /**
     * 使用Keytab进行认证
     *
     * @throws Exception
     */
    public void loginImplWithKeytab() throws LoginException {
        // 设置系统属性
        // 配置java.security.krb5.conf
        System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
        // 开启sun.security.krb5.debug
        System.setProperty("sun.security.krb5.debug", "true");

        // keytab
//        optionMap.put("keytab", "/home/stone/foobar.keytab");
        optionMap.put("keyTab", "/home/stone/stone.keytab");
        // principal
//        optionMap.put("principal", "foobar/foobar.analyticservice.net@ANALYTICSERVICE.NET");
        optionMap.put("principal", "stone/stone.analyticservice.net@ANALYTICSERVICE.NET");
        // others
//        optionMap.put("doNotPrompt", "true");
//        optionMap.put("refreshKrb5Config", "true");
//        optionMap.put("useTicketCache", "false");
//        optionMap.put("renewTGT", "false");
        optionMap.put("useKeyTab", "true");
        // storeKey=true需要配置useKeytab=false使用密码登录才可以
        optionMap.put("storeKey", "false");
        optionMap.put("isInitiator", "true");
        optionMap.put("debug", "true");

        // 执行
        initAndLoginAndCommitWithPrint(subject, krb5LoginModule, sharedState, optionMap);
    }

    /**
     * 使用用户和密码进行登录
     *
     * @throws Exception
     */
    public void loginImplWithPassword() throws LoginException {
        // others
        // 使用password登录这里useKeytab=false
        optionMap.put("useKeytab", "false");
        optionMap.put("doNotPrompt", "true");
        optionMap.put("refreshKrb5Config", "true");
        optionMap.put("useTicketCache", "true");
        optionMap.put("renewTGT", "false");
        // storeKey=true需要配置useKeytab=false使用密码登录才可以
        optionMap.put("storeKey", "true");
        optionMap.put("isInitiator", "true");
        optionMap.put("debug", "true");
        // 使用password认证时需要，并且要配置下边的sharedState
        optionMap.put("useFirstPass", "true");
        optionMap.put("tryFirstPass", "true");
        optionMap.put("storePass", "true");

        // 定义登录需要的用户/密码
        final String username = "stone/stone.analyticservice.net@ANALYTICSERVICE.NET";
        final String password = "stone";

        // 通过sharedState来传递以下2个参数
        // 这里需要注意密码必须有字符串转换为字符数组
        sharedState.put("javax.security.auth.login.password", password.toCharArray());
        sharedState.put("javax.security.auth.login.name", username);
        // 也可以设置principal来设置username
        optionMap.put("principal", username);

        // 执行
        initAndLoginAndCommitWithPrint(subject, krb5LoginModule, sharedState, optionMap);
    }

    /**
     * krb5LoginModule初始化以及登录和提交的操作
     *
     * @param subject         抽象的认证主体
     * @param krb5LoginModule 登录模块
     * @param sharedState     用于值传递,比如用户/密码
     * @param optionMap       krb5一些配置项
     * @throws LoginException
     */
    private void initAndLoginAndCommitWithPrint(Subject subject, Krb5LoginModule krb5LoginModule, Map<String, Object> sharedState, Map<String, String> optionMap) throws LoginException {

//        KerberosPrincipal kerberosPrincipal = new KerberosPrincipal();

        CustomKrb5LoginModule customKrb5LoginModule = new CustomKrb5LoginModule();
        customKrb5LoginModule.initialize(subject, null, sharedState, optionMap);
        boolean loginResult = customKrb5LoginModule.login();

        // 初始化
//        krb5LoginModule.initialize(subject, null, sharedState, optionMap);
        // 登录
//        boolean loginResult = krb5LoginModule.login();
        System.out.println("login: " + loginResult);

        // 提交
        boolean commitResult = krb5LoginModule.commit();
        System.out.println("commit: " + commitResult);

        // 输出subject
        System.out.println("subject = " + subject);
    }


}

